﻿namespace OnlineVideos.RingToneEditor
{
    using System;

    public class XingHeader
    {
        private int bytesOffset = -1;
        private int endOffset;
        private Mp3Frame frame;
        private int framesOffset = -1;
        private static int[] sr_table = new int[] { 0xac44, 0xbb80, 0x7d00, 0x1869f };
        private int startOffset;
        private int tocOffset = -1;
        private int vbrScale = -1;

        private XingHeader()
        {
        }

        public static XingHeader LoadXingHeader(Mp3Frame frame)
        {
            XingHeader header = new XingHeader {
                frame = frame
            };
            int index = 0;
            if (frame.MpegVersion == MpegVersion.Version1)
            {
                if (frame.ChannelMode != ChannelMode.Mono)
                {
                    index = 0x24;
                }
                else
                {
                    index = 0x15;
                }
            }
            else if (frame.MpegVersion == MpegVersion.Version2)
            {
                if (frame.ChannelMode != ChannelMode.Mono)
                {
                    index = 0x15;
                }
                else
                {
                    index = 13;
                }
            }
            else
            {
                return null;
            }
            if ((((frame.RawData[index] == 0x58) && (frame.RawData[index + 1] == 0x69)) && (frame.RawData[index + 2] == 110)) && (frame.RawData[index + 3] == 0x67))
            {
                header.startOffset = index;
                index += 4;
            }
            else
            {
                return null;
            }
            XingHeaderOptions options = (XingHeaderOptions) ReadBigEndian(frame.RawData, index);
            index += 4;
            if ((options & XingHeaderOptions.Frames) != 0)
            {
                header.framesOffset = index;
                index += 4;
            }
            if ((options & XingHeaderOptions.Bytes) != 0)
            {
                header.bytesOffset = index;
                index += 4;
            }
            if ((options & XingHeaderOptions.Toc) != 0)
            {
                header.tocOffset = index;
                index += 100;
            }
            if ((options & XingHeaderOptions.VbrScale) != 0)
            {
                header.vbrScale = ReadBigEndian(frame.RawData, index);
                index += 4;
            }
            header.endOffset = index;
            return header;
        }

        private static int ReadBigEndian(byte[] buffer, int offset)
        {
            int num = buffer[offset];
            num = num << 8;
            num |= buffer[offset + 1];
            num = num << 8;
            num |= buffer[offset + 2];
            num = num << 8;
            return (num | buffer[offset + 3]);
        }

        private void WriteBigEndian(byte[] buffer, int offset, int value)
        {
            byte[] bytes = BitConverter.GetBytes(value);
            for (int i = 0; i < 4; i++)
            {
                buffer[(offset + 4) - i] = bytes[i];
            }
        }

        public int Bytes
        {
            get
            {
                if (this.bytesOffset == -1)
                {
                    return -1;
                }
                return ReadBigEndian(this.frame.RawData, this.bytesOffset);
            }
            set
            {
                if (this.framesOffset == -1)
                {
                    throw new InvalidOperationException("Bytes flag is not set");
                }
                this.WriteBigEndian(this.frame.RawData, this.bytesOffset, value);
            }
        }

        public int Frames
        {
            get
            {
                if (this.framesOffset == -1)
                {
                    return -1;
                }
                return ReadBigEndian(this.frame.RawData, this.framesOffset);
            }
            set
            {
                if (this.framesOffset == -1)
                {
                    throw new InvalidOperationException("Frames flag is not set");
                }
                this.WriteBigEndian(this.frame.RawData, this.framesOffset, value);
            }
        }

        public Mp3Frame Mp3Frame
        {
            get
            {
                return this.frame;
            }
        }

        public int VbrScale
        {
            get
            {
                return this.vbrScale;
            }
        }

        [Flags]
        private enum XingHeaderOptions
        {
            Bytes = 2,
            Frames = 1,
            Toc = 4,
            VbrScale = 8
        }
    }
}

